package com.foreverwin.mes.production.support;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.foreverwin.mes.common.enums.HandleEnum;
import com.foreverwin.mes.core.exception.BusinessException;
import com.foreverwin.mes.core.utils.CommonMethods;
import com.foreverwin.mes.core.utils.DateUtil;
import com.foreverwin.mes.core.utils.SpringUtil;
import com.foreverwin.mes.meapi.model.CustomFields;
import com.foreverwin.mes.meapi.model.Inventory;
import com.foreverwin.mes.meapi.model.InventoryAssyData;
import com.foreverwin.mes.meapi.model.Sfc;
import com.foreverwin.mes.meapi.service.CustomFieldsService;
import com.foreverwin.mes.meapi.service.InventoryAssyDataService;
import com.foreverwin.mes.meapi.service.InventoryService;
import com.foreverwin.mes.meapi.service.SfcInWorkService;
import com.foreverwin.mes.production.constant.Constants;
import com.foreverwin.mes.production.dto.ResourceInventoryDto;
import com.foreverwin.mes.production.model.MaterialConsumption;
import com.foreverwin.mes.production.model.ResourceInventory;
import com.foreverwin.mes.production.model.ResourceInventoryLog;
import com.foreverwin.mes.production.service.MaterialConsumptionService;
import com.foreverwin.mes.production.service.ResourceInventoryLogService;
import com.foreverwin.mes.production.service.ResourceInventoryService;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * @author Ervin Chen
 * @date 2020/2/5 12:03
 */
public class ItemBatchNumberQtyUnloadStrategy implements UnloadStrategy {

    private InventoryService inventoryService;
    private ResourceInventoryService resourceInventoryService;
    private ResourceInventoryLogService resourceInventoryLogService;
    private MaterialConsumptionService materialConsumptionService;
    private SfcInWorkService sfcInWorkService;
    private CustomFieldsService customFieldsService;
    private InventoryAssyDataService inventoryAssyDataService;

    @Override
    public void initService() {
        inventoryService = SpringUtil.getBean(InventoryService.class);
        resourceInventoryService = SpringUtil.getBean(ResourceInventoryService.class);
        resourceInventoryLogService = SpringUtil.getBean(ResourceInventoryLogService.class);
        materialConsumptionService = SpringUtil.getBean(MaterialConsumptionService.class);
        sfcInWorkService = SpringUtil.getBean(SfcInWorkService.class);
        customFieldsService = SpringUtil.getBean(CustomFieldsService.class);
        inventoryAssyDataService = SpringUtil.getBean(InventoryAssyDataService.class);
    }

    @Override
    public boolean validate(ResourceInventoryDto resourceInventoryDto) {
        String site = CommonMethods.getSite();
        String resource = resourceInventoryDto.getResrce();
        String itemBo = resourceInventoryDto.getItemBo();
        String vendorLot = resourceInventoryDto.getVendorLot();
        Double qty = resourceInventoryDto.getQty();
        String inventoryId = resourceInventoryDto.getInventoryId();

        if (StringUtils.isBlank(inventoryId)) {
            throw BusinessException.build("production.resourceInventory.inventoryNotBlank", new Object[]{});
        }
        if (qty == null) {
            throw BusinessException.build("production.resourceInventory.unloadQtyNotBlank", new Object[]{});
        }
        if (qty <= 0) {
            throw BusinessException.build("production.resourceInventory.unloadQtyNotValid", qty);
        }

        String resourceInventoryHandle = HandleEnum.RESOURCE_INVENTORY.getHandle(site, resource, itemBo, vendorLot, inventoryId);
        ResourceInventory resourceInventory = resourceInventoryService.selectById(resourceInventoryHandle);
        Double qtyOnHand = resourceInventory.getQtyOnHand();

        //校验卸料数量是否超过理论剩余量上料偏差范围
        double upperDeviation = resourceInventoryService.getItemUpperDeviation(itemBo);
        double lowerDeviation = resourceInventoryService.getItemLowerDeviation(itemBo);
        double upperLimit = BigDecimal.valueOf(qtyOnHand).multiply(BigDecimal.valueOf(1 + upperDeviation / 100)).doubleValue();
        double lowerLimit = BigDecimal.valueOf(qtyOnHand).multiply(BigDecimal.valueOf(1 - lowerDeviation / 100)).doubleValue();
        if (qty < lowerLimit || qty > upperLimit) {
            throw BusinessException.build("production.resourceInventory.unloadQtyOverLimit", qty, lowerLimit, upperLimit);
        }

        //偏差算入损耗，计入设备最后一个sfc的物料消耗
        double lossQty = qtyOnHand - qty;
        if (lossQty != 0) {
            addMaterialConsumption(resourceInventoryDto, lossQty);
        }

        return true;
    }

    @Override
    public boolean unload(ResourceInventoryDto resourceInventoryDto) {
        String resource = resourceInventoryDto.getResrce();
        String itemBo = resourceInventoryDto.getItemBo();
        String inventoryId = resourceInventoryDto.getInventoryId();
        String vendorLot = resourceInventoryDto.getVendorLot();
        Double qty = resourceInventoryDto.getQty();
        String loadMode = resourceInventoryDto.getLoadMode();
        String site = CommonMethods.getSite();
        String userBo = CommonMethods.getUserBo();
        Date now = new Date();

        //卸料数量返回库存
        increaseInventory(inventoryId, qty);

        //删除设备库存
        String resourceInventoryHandle = HandleEnum.RESOURCE_INVENTORY.getHandle(site, resource, itemBo, vendorLot, inventoryId);
        resourceInventoryService.deleteById(resourceInventoryHandle);

        //保存上料记录
        String resourceInventoryLogHandle = HandleEnum.RESOURCE_INVENTORY_LOG.getHandle(site, resource, itemBo, vendorLot, DateUtil.format(now));
        ResourceInventoryLog resourceInventoryLog = new ResourceInventoryLog();
        resourceInventoryLog.setHandle(resourceInventoryLogHandle);
        resourceInventoryLog.setSite(site);
        resourceInventoryLog.setResrce(resource);
        resourceInventoryLog.setItemBo(itemBo);
        resourceInventoryLog.setInventoryId(inventoryId);
        resourceInventoryLog.setVendorLot(vendorLot);
        resourceInventoryLog.setQty(qty * -1);
        resourceInventoryLog.setLoadMode(loadMode);
        resourceInventoryLog.setCreatedDateTime(now);
        resourceInventoryLog.setCreateUserBo(userBo);
        resourceInventoryLogService.insert(resourceInventoryLog);

        return true;
    }

    public boolean increaseInventory(String inventoryId, Double qty) {
        String site = CommonMethods.getSite();
        String handle = HandleEnum.INVENTORY.getHandle(site, inventoryId);
        Inventory inventory = inventoryService.selectById(handle);
        inventory.setQtyOnHand(inventory.getQtyOnHand() + qty);
        return inventoryService.updateById(inventory);
    }

    /**
     * 卸料偏差存入物料消耗表
     *
     * @param resourceInventoryDto
     * @param costQty
     */
    public void addMaterialConsumption(ResourceInventoryDto resourceInventoryDto, double costQty) {
        String resource = resourceInventoryDto.getResrce();
        String inventoryId = resourceInventoryDto.getInventoryId();
        String site = CommonMethods.getSite();
        String userBo = CommonMethods.getUserBo();
        Date now = new Date();
        Sfc sfcDo = null;

        //查找当前设备生产sfc
        if (resourceInventoryService.isProductionResource(site, resource)) {
            List<Sfc> sfcs = sfcInWorkService.selectSfc(HandleEnum.RESOURCE.getHandle(site, resource));
            if (sfcs == null || sfcs.isEmpty()) {
                throw BusinessException.build("production.resourceInventory.resourceNotExistSfcInWork", resource);
            }
            sfcDo = sfcs.get(0);
        } else {
            EntityWrapper<CustomFields> entityWrapper = new EntityWrapper<>();
            entityWrapper.eq("attribute", "");
            Optional<String> customField = customFieldsService.selectList(entityWrapper)
                    .stream()
                    .filter(customFields -> site.equals(HandleEnum.RESOURCE.getPart(customFields.getHandle(), 0)))
                    .filter(customFields -> Arrays.asList(customFields.getValue().split(",")).contains(resource))
                    .map(CustomFields::getHandle)
                    .findFirst();
            if (!customField.isPresent()) {
                throw BusinessException.build("production.resourceInventory.loadResourceNotExistProductionResource", resource);
            } else {
                List<Sfc> sfcs = sfcInWorkService.selectSfc(customField.get());
                if (sfcs == null || sfcs.isEmpty()) {
                    throw BusinessException.build("production.resourceInventory.resourceNotExistSfcInWork", HandleEnum.RESOURCE.getPart(customField.get(), 1));
                }
                sfcDo = sfcs.get(0);
            }
        }
        String shopOrderBo = sfcDo.getShopOrderBo();
        String itemBo = sfcDo.getItemBo();
        String componentBo = resourceInventoryDto.getItemBo();

        String inventoryBo = HandleEnum.INVENTORY.getHandle(site, inventoryId);
        Inventory inventory = inventoryService.selectById(inventoryBo);
        String storageLocationBo = inventory.getStorageLocationBo();
        String storageLocation = StringUtils.isBlank(storageLocationBo) ? null : HandleEnum.STORAGE_LOCATION.getPart(storageLocationBo, 1);

        String batchNumber = null;
        InventoryAssyData inventoryAssyDataQuerier = new InventoryAssyData();
        inventoryAssyDataQuerier.setInventoryBo(inventoryBo);
        inventoryAssyDataQuerier.setDataField(Constants.BATCH_NUMBER);
        Optional<InventoryAssyData> inventoryAssyDataOpt = inventoryAssyDataService.selectList(inventoryAssyDataQuerier)
                .stream()
                .findFirst();
        if (inventoryAssyDataOpt.isPresent()) {
            batchNumber = inventoryAssyDataOpt.get().getDataAttr();
        }

        MaterialConsumption materialConsumption = new MaterialConsumption();
        materialConsumption.setHandle(HandleEnum.MATERIAL_CONSUMPTION.getHandle(site, sfcDo.getSfc(), DateUtil.format(now)));
        materialConsumption.setSite(site);
        materialConsumption.setShopOrder(HandleEnum.SHOP_ORDER.getPart(shopOrderBo, 1));
        materialConsumption.setSfc(sfcDo.getSfc());
        materialConsumption.setItem(HandleEnum.ITEM.getPart(itemBo, 1));
        materialConsumption.setComponent(HandleEnum.ITEM.getPart(componentBo, 1));
        materialConsumption.setInventoryId(inventoryId);
        materialConsumption.setBatchNumber(batchNumber);
        materialConsumption.setStorageLocation(storageLocation);
        materialConsumption.setQty(costQty);
        materialConsumption.setSendErp("N");
        materialConsumption.setCreateUserBo(userBo);
        materialConsumption.setCreatedDateTime(now);
        materialConsumptionService.insert(materialConsumption);
    }
}
